home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / perl5 / Net / DBus / RemoteObject.pm < prev    next >
Encoding:
Perl POD Document  |  2008-02-20  |  11.1 KB  |  423 lines

  1. # -*- perl -*-
  2. #
  3. # Copyright (C) 2004-2006 Daniel P. Berrange
  4. #
  5. # This program is free software; You can redistribute it and/or modify
  6. # it under the same terms as Perl itself. Either:
  7. #
  8. # a) the GNU General Public License as published by the Free
  9. #   Software Foundation; either version 2, or (at your option) any
  10. #   later version,
  11. #
  12. # or
  13. #
  14. # b) the "Artistic License"
  15. #
  16. # The file "COPYING" distributed along with this file provides full
  17. # details of the terms and conditions of the two licenses.
  18.  
  19. =pod
  20.  
  21. =head1 NAME
  22.  
  23. Net::DBus::RemoteObject - Access objects provided on the bus
  24.  
  25. =head1 SYNOPSIS
  26.  
  27.   my $service = $bus->get_service("org.freedesktop.DBus");
  28.   my $object = $service->get_object("/org/freedesktop/DBus");
  29.  
  30.   print "Names on the bus {\n";
  31.   foreach my $name (sort @{$object->ListNames}) {
  32.       print "  ", $name, "\n";
  33.   }
  34.   print "}\n";
  35.  
  36. =head1 DESCRIPTION
  37.  
  38. This module provides the API for accessing remote objects available
  39. on the bus. It uses the autoloader to fake the presence of methods
  40. based on the API of the remote object. There is also support for
  41. setting callbacks against signals, and accessing properties of the
  42. object.
  43.  
  44. =head1 METHODS
  45.  
  46. =over 4
  47.  
  48. =cut
  49.  
  50. package Net::DBus::RemoteObject;
  51.  
  52. use 5.006;
  53. use strict;
  54. use warnings;
  55.  
  56. our $AUTOLOAD;
  57.  
  58. use Net::DBus::Binding::Introspector;
  59. use Net::DBus::ASyncReply;
  60. use Net::DBus::Annotation qw(:call);
  61.  
  62.  
  63. =item my $object = Net::DBus::RemoteObject->new($service, $object_path[, $interface]);
  64.  
  65. Creates a new handle to a remote object. The C<$service> parameter is an instance
  66. of the L<Net::DBus::RemoteService> method, and C<$object_path> is the identifier of
  67. an object exported by this service, for example C</org/freedesktop/DBus>. For remote
  68. objects which implement more than one interface it is possible to specify an optional
  69. name of an interface as the third parameter. This is only really required, however, if
  70. two interfaces in the object provide methods with the same name, since introspection
  71. data can be used to automatically resolve the correct interface to call cases where
  72. method names are unique. Rather than using this constructor directly, it is preferrable
  73. to use the C<get_object> method on L<Net::DBus::RemoteService>, since this caches handles
  74. to remote objects, eliminating unneccessary introspection data lookups.
  75.  
  76. =cut
  77.  
  78.  
  79. sub new {
  80.     my $proto = shift;
  81.     my $class = ref($proto) || $proto;
  82.     my $self = {};
  83.  
  84.     $self->{service} = shift;
  85.     $self->{object_path}  = shift;
  86.     $self->{interface} = @_ ? shift : undef;
  87.     $self->{introspected} = 0;
  88.  
  89.     bless $self, $class;
  90.  
  91.     return $self;
  92. }
  93.  
  94. =item my $object = $object->as_interface($interface);
  95.  
  96. Casts the object to a specific interface, returning a new instance of the
  97. L<Net::DBus::RemoteObject> specialized to the desired interface. It is only
  98. neccessary to cast objects to a specific interface, if two interfaces
  99. export methods or signals with the same name, or the remote object does not
  100. support introspection.
  101.  
  102. =cut
  103.  
  104. sub as_interface {
  105.     my $self = shift;
  106.     my $interface = shift;
  107.  
  108.     die "already cast to " . $self->{interface} . "'"
  109.     if $self->{interface};
  110.  
  111.     return $self->new($self->{service},
  112.               $self->{object_path},
  113.               $interface);
  114. }
  115.  
  116. =item my $service = $object->get_service
  117.  
  118. Retrieves a handle for the remote service on which this object is
  119. attached. The returned object is an instance of L<Net::DBus::RemoteService>
  120.  
  121. =cut
  122.  
  123. sub get_service {
  124.     my $self = shift;
  125.     return $self->{service};
  126. }
  127.  
  128. =item my $path = $object->get_object_path
  129.  
  130. Retrieves the unique path identifier for this object within the
  131. service.
  132.  
  133. =cut
  134.  
  135. sub get_object_path {
  136.     my $self = shift;
  137.     return $self->{object_path};
  138. }
  139.  
  140. =item my $object = $object->get_child_object($subpath, [$interface])
  141.  
  142. Retrieves a handle to a child of this object, identified
  143. by the relative path C<$subpath>. The returned object
  144. is an instance of C<Net::DBus::RemoteObject>. The optional
  145. C<$interface> parameter can be used to immediately cast
  146. the object to a specific type.
  147.  
  148. =cut
  149.  
  150. sub get_child_object {
  151.     my $self = shift;
  152.     my $path = shift;
  153.     my $interface = @_ ? shift : undef;
  154.     my $fullpath = $self->{object_path} . $path;
  155.  
  156.     return $self->new($self->get_service,
  157.               $fullpath,
  158.               $interface);
  159. }
  160.  
  161. sub _introspector {
  162.     my $self = shift;
  163.  
  164.  
  165.     unless ($self->{introspected}) {
  166.     my $con = $self->{service}->get_bus()->get_connection();
  167.  
  168.     my $call = $con->make_method_call_message($self->{service}->get_service_name(),
  169.                           $self->{object_path},
  170.                           "org.freedesktop.DBus.Introspectable",
  171.                           "Introspect");
  172.  
  173.     my $xml = eval {
  174.         my $reply = $con->send_with_reply_and_block($call, 60 * 1000);
  175.  
  176.         my $iter = $reply->iterator;
  177.         return $iter->get(&Net::DBus::Binding::Message::TYPE_STRING);
  178.     };
  179.     if ($@) {
  180.         if (UNIVERSAL::isa($@, "Net::DBus::Error") &&
  181.         $@->{name} eq "org.freedesktop.DBus.Error.ServiceUnknown") {
  182.         die $@;
  183.         } else {
  184.         # Ignore other failures, since its probably
  185.         # just that the object doesn't implement
  186.         # the introspect method. Of course without
  187.         # the introspect method we can't tell for sure
  188.         # if this is the case..
  189.         #warn "could not introspect object: $@";
  190.         }
  191.     }
  192.     if ($xml) {
  193.         $self->{introspector} = Net::DBus::Binding::Introspector->new(xml => $xml,
  194.                                       object_path => $self->{object_path});
  195.     }
  196.     $self->{introspected} = 1;
  197.     }
  198.     return $self->{introspector};
  199. }
  200.  
  201.  
  202. =item $object->connect_to_signal($name, $coderef);
  203.  
  204. Connects a callback to a signal emitted by the object. The C<$name>
  205. parameter is the name of the signal within the object, and C<$coderef>
  206. is a reference to an anonymous subroutine. When the signal C<$name>
  207. is emitted by the remote object, the subroutine C<$coderef> will be
  208. invoked, and passed the parameters from the signal.
  209.  
  210. =cut
  211.  
  212. sub connect_to_signal {
  213.     my $self = shift;
  214.     my $name = shift;
  215.     my $code = shift;
  216.  
  217.     my $ins = $self->_introspector;
  218.     my $interface = $self->{interface};
  219.     if (!$interface) {
  220.     if (!$ins) {
  221.         die "no introspection data available for '" . $self->get_object_path .
  222.         "', and object is not cast to any interface";
  223.     }
  224.     my @interfaces = $ins->has_signal($name);
  225.  
  226.     if ($#interfaces == -1) {
  227.         die "no signal with name '$name' is exported in object '" .
  228.         $self->get_object_path . "'\n";
  229.     } elsif ($#interfaces > 0) {
  230.         warn "signal with name '$name' is exported " .
  231.         "in multiple interfaces of '" . $self->get_object_path . "'" .
  232.         "connecting to first interface only\n";
  233.     }
  234.     $interface = $interfaces[0];
  235.     }
  236.  
  237.     if ($ins &&
  238.     $ins->has_signal($name, $interface) &&
  239.     $ins->is_signal_deprecated($name, $interface)) {
  240.     warn "signal $name in interface $interface on " . $self->get_object_path . " is deprecated";
  241.     }
  242.  
  243.     $self->get_service->
  244.     get_bus()->
  245.     _add_signal_receiver(sub {
  246.         my $signal = shift;
  247.         my $ins = $self->_introspector;
  248.         my @params;
  249.         if ($ins) {
  250.         @params = $ins->decode($signal, "signals", $signal->get_member, "params");
  251.         } else {
  252.         @params = $signal->get_args_list;
  253.         }
  254.         &$code(@params);
  255.     },
  256.                  $name,
  257.                  $interface,
  258.                  $self->{service}->get_owner_name(),
  259.                  $self->{object_path});
  260. }
  261.  
  262.  
  263. sub DESTROY {
  264.     # No op merely to stop AutoLoader trying to
  265.     # call DESTROY on remote object
  266. }
  267.  
  268. sub AUTOLOAD {
  269.     my $self = shift;
  270.     my $sub = $AUTOLOAD;
  271.  
  272.     my $mode = dbus_call_sync;
  273.     if (@_ && UNIVERSAL::isa($_[0], "Net::DBus::Annotation")) {
  274.     $mode = shift;
  275.     }
  276.  
  277.     (my $name = $AUTOLOAD) =~ s/.*:://;
  278.  
  279.     my $interface = $self->{interface};
  280.  
  281.     # If introspection data is available, use that
  282.     # to resolve correct interface (if object is not
  283.     # cast to an explicit interface already)
  284.     my $ins = $self->_introspector();
  285.     if ($ins) {
  286.     if ($interface) {
  287.         if ($ins->has_method($name, $interface)) {
  288.         return $self->_call_method($mode, $name, $interface, 1, @_);
  289.         }
  290.         if ($ins->has_property($name, $interface)) {
  291.         if ($ins->is_property_deprecated($name, $interface)) {
  292.             warn "property $name in interface $interface on " . $self->get_object_path . " is deprecated";
  293.         }
  294.  
  295.         if (@_) {
  296.             $self->_call_method($mode, "Set", "org.freedesktop.DBus.Properties", $interface, 1, $name, $_[0]);
  297.             return ();
  298.         } else {
  299.             return $self->_call_method($mode, "Get", "org.freedesktop.DBus.Properties", $interface, 1, $name);
  300.         }
  301.         }
  302.     } else {
  303.         my @interfaces = $ins->has_method($name);
  304.  
  305.         if (@interfaces) {
  306.         if ($#interfaces > 0) {
  307.             die "method with name '$name' is exported " .
  308.             "in multiple interfaces of '" . $self->get_object_path . "'";
  309.         }
  310.         return $self->_call_method($mode, $name, $interfaces[0], 1, @_);
  311.         }
  312.         @interfaces = $ins->has_property($name);
  313.  
  314.         if (@interfaces) {
  315.         if ($#interfaces > 0) {
  316.             die "property with name '$name' is exported " .
  317.             "in multiple interfaces of '" . $self->get_object_path . "'";
  318.         }
  319.         $interface = $interfaces[0];
  320.         if ($ins->is_property_deprecated($name, $interface)) {
  321.             warn "property $name in interface $interface on " . $self->get_object_path . " is deprecated";
  322.         }
  323.         if (@_) {
  324.             $self->_call_method($mode, "Set", "org.freedesktop.DBus.Properties", $interface, 1, $name, $_[0]);
  325.             return ();
  326.         } else {
  327.             return $self->_call_method($mode, "Get", "org.freedesktop.DBus.Properties", $interface, 1, $name);
  328.         }
  329.         }
  330.     }
  331.     }
  332.  
  333.     if (!$interface) {
  334.     die "no introspection data available for method '" . $name . "' in object '" .
  335.         $self->get_object_path . "', and object is not cast to any interface";
  336.     }
  337.  
  338.     return $self->_call_method($mode, $name, $interface, 0, @_);
  339. }
  340.  
  341.  
  342. sub _call_method {
  343.     my $self = shift;
  344.     my $mode = shift;
  345.     my $name = shift;
  346.     my $interface = shift;
  347.     my $introspect = shift;
  348.  
  349.     my $con = $self->{service}->get_bus()->get_connection();
  350.  
  351.     my $ins = $introspect ? $self->_introspector : undef;
  352.     if ($ins &&
  353.     $ins->is_method_deprecated($name, $interface)) {
  354.     warn "method '$name' in interface $interface on object " . $self->get_object_path . " is deprecated\n";
  355.     }
  356.  
  357.     my $call = $con->make_method_call_message($self->{service}->get_service_name(),
  358.                           $self->{object_path},
  359.                           $interface,
  360.                           $name);
  361.  
  362.     #$call->set_destination($self->get_service->get_owner_name);
  363.  
  364.     if ($ins) {
  365.     $ins->encode($call, "methods", $name, "params", @_);
  366.     } else {
  367.     $call->append_args_list(@_);
  368.     }
  369.  
  370.     if ($mode == dbus_call_sync) {
  371.     my $reply = $con->
  372.         send_with_reply_and_block($call, 60 * 1000);
  373.  
  374.     my @reply;
  375.     if ($ins) {
  376.         @reply = $ins->decode($reply, "methods", $name, "returns");
  377.     } else {
  378.         @reply = $reply->get_args_list;
  379.     }
  380.  
  381.     return wantarray ? @reply : $reply[0];
  382.     } elsif ($mode == dbus_call_async) {
  383.     my $pending_call = $self->{service}->
  384.         get_bus()->
  385.         get_connection()->
  386.         send_with_reply($call, 60 * 1000);
  387.     my $reply = Net::DBus::ASyncReply->_new(pending_call => $pending_call,
  388.                         ($ins ? (introspector => $ins,
  389.                              method_name => $name)
  390.                          : ()));
  391.     return $reply;
  392.     } elsif ($mode == dbus_call_noreply) {
  393.     $call->set_no_reply(1);
  394.     $self->{service}->
  395.         get_bus()->
  396.         get_connection()->
  397.         send($call, 60 * 1000);
  398.     } else {
  399.     die "unsupported annotation '$mode'";
  400.     }
  401. }
  402.  
  403.  
  404. 1;
  405.  
  406. =pod
  407.  
  408. =back
  409.  
  410. =head1 AUTHOR
  411.  
  412. Daniel Berrange <dan@berrange.com>
  413.  
  414. =head1 COPYRIGHT
  415.  
  416. Copright (C) 2004-2005, Daniel Berrange.
  417.  
  418. =head1 SEE ALSO
  419.  
  420. L<Net::DBus::RemoteService>, L<Net::DBus::Object>
  421.  
  422. =cut
  423.